package com.jingdianjichi.subject.domain.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * RedisUtil工具类,这样我做复杂处理的时候,是非常方便的亲
 *
 * @author: ChickenWing
 * @date: 2023/1/15
 */
@Component
@Slf4j
public class RedisUtil {

    //redis客户端
    @Resource
    private RedisTemplate redisTemplate;

    //用于构建key的
    private static final String CACHE_KEY_SEPARATOR = ".";

    /**
     * 构建缓存key
     */
    public String buildKey(String... strObjs) {
        return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR)); //根据.去拼接
    }

    /**
     * 是否存在key
     */
    public boolean exist(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除key
     */
    public boolean del(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * set一个key(不带过期)
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * set一个key(带过期) timeUnit是单位
     */
    public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);
        //只有在该键不存在的情况下才会对其进行设置
    }

    /**
     * 获取String类的缓存
     */
    public String get(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    /**
     * 下面是其他数据类型的工具类了,ZSet是有序集合,一个key可以有多个值的
     */

    //有序集合（ZSet),score是分数,从而实现排序的
    public Boolean zAdd(String key, String value, Long score) {
        return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));
    }

    /**
     * 获取指定ZSet中元素的总数。
     */
    public Long countZset(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 从指定的ZSet中返回一个范围内的元素。
     */
    public Set<String> rangeZset(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 从指定的ZSet中删除一个元素。
     */
    public Long removeZset(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * 从指定的ZSet中删除一组元素。
     */
    public void removeZsetList(String key, Set<String> value) {
        value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));
    }

    /**
     *获取指定ZSet中某个元素的分值。
     */
    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 按分值范围返回ZSet中的元素。
     */
    public Set<String> rangeByScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));
    }

    /**
     * 增加ZSet中某个元素的分值。
     */
    public Object addScore(String key, Object value, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * 获取ZSet中某个元素的排名（索引）。
     */
    public Object rank(String key, Object obj) {
        return redisTemplate.opsForZSet().rank(key, obj);
    }


    /**
     *获取ZSet中某个元素的排名和分数一起返回过来
     */
    public Set<ZSetOperations.TypedTuple<String>> rankWithScore(String key,long start,long end){
        Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);

        return set;
        /*
        具体功能是获取指定key对应的有序集合中排名在start和end之间的成员及其分数，并以TypedTuple对象的形式返回。其中，TypedTuple是Spring Data Redis提供的用于表示集合中的元素和分数的接口
         */
    }


    /**
     * 下面是和hash做交互的
     */
    //存入hash
    public void pushHash(String key,String hashKey,Object hashVal){
        redisTemplate.opsForHash().put(key,hashKey,hashVal);
    }


    //读取key下的数量亲,这里是返回的int类型的,上面是返回String类型的
    public Integer getInt(String key){
        return (Integer) redisTemplate.opsForValue().get(key);
    }

    /**
     * 给,目标key增加点赞的数量,count就是加多少来的
     */
    public void increment(String key,Integer count){
        redisTemplate.opsForValue().increment(key,count);
    }

    /**
     * 获得hash的数据并删除,因为扫表了
     */
    public Map<Object, Object> getHashAndDelete(String key) {
        Map<Object, Object> map=new HashMap<>();

        //key对用的数据,返回游标亲,为了和map做结合我们传递一个泛型过去
        Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan(key, ScanOptions.NONE);

        //变量
        while (cursor.hasNext()){
            Map.Entry<Object, Object> entry = cursor.next();
            String hashKey = (String) entry.getKey();           //题目id和用户id
            Integer value = (Integer) entry.getValue();         //是否被点赞

            map.put(hashKey,value);

            redisTemplate.opsForHash().delete(key,hashKey);
        }

        return map;
    }
}